--[[##################################################################################

	WebLua
	© 2011 alex82
	http://mydc.ru/topic4750.html

	Данный файл содержит низкоуровневые функции, необходимые при использовании Lua для
	создания динамических web-страниц

####################################################################################

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

####################################################################################

	Таблицы:

	_SERVER	- Окружение, полученное от web-сервера
	_HEADERS	- Заголовки HTTP. По умолчанию в таблице содержится один заголовок: content-type: text/html.
			Все заголовки должны быть установлены до отправки каких-либо данных web-серверу.
			На момент первой отправки данных в таблице должен содержаться как минимум один заголовок, в противном случае web-сервер сообщит о внутренней ошибке.
	_GET	- Переменные, полученные при помощи метода GET.
	_POST	- Переменные, полученные при помощи метода POST.
	_REQUEST	- Переменные, полученные при помощи методов GET и POST.
	_COOKIE	- Куки, сохраненные на стороне клиента.
	_SET_COOKIE	- Куки, которые необходимо сохранить на стороне клиента.
			Каждое значение должно быть таблицей, содержащей собственно значение куки и время истечения в формате unix-epoch.
			Если время истечения не указано, куки сохраняется до конца сессии.
			Для удаления куки, установите его заведомо просроченным.

	В таблицах _HEADERS,_GET,_POST,_REQUEST и _COOKIE все индексы хранятся в нижнем регистре.
	Таблицы _SERVER,_GET,_POST,_REQUEST и _COOKIE заполняются данными лишь при необходимости, поэтому к ним нельзя применять функции pairs и next.


	Функции:

	echo(...)	- Функция, использующаяся для отправки данных клиенту.
			При первой отправке данных помимо самих данных отправляются заголовки, после чего добавление или изменение заголовков становится невозможным, а данная функция становится ссылкой на функцию io.write
	print(...)	- Функция, использующаяся для отображения произвольных значений. Результат помещается в теги <pre><code></code></pre>. Данную функцию необходимо использовать только в целях отладки.
	tprint(tTable[, sTableName])	- Функция отображает содержимое таблицы tTable. Данную функцию необходимо использовать только в целях отладки.


	Переменные:

	_HEADERS_SENT	- Переменная, определяющая, были ли отправлены заголовки. Менять значение этой переменной вручную не рекомендуется.

###################################################################################]]

_SERVER,_HEADERS,_HEADERS_SENT,_GET,_POST,_REQUEST,_COOKIE,_SET_COOKIE = {},{},false,{},{},{},{},{}

local tHex = {[10] = "A",[11] = "B",[12] = "C",[13] = "D",[14] = "E",[15] = "F"}
local function encode(str)
	return str:gsub("[^a-zA-Z0-9 *%-%._]",function(s)
		local b = s:byte()
		local b1,b2 = math.floor(b/16),math.fmod(b,16)
		return "%"..(tHex[b1] or b1)..(tHex[b2] or b2)
	end):gsub(" ","+")
end

setmetatable(_HEADERS,{
	__newindex = function(t,k,v)
		if _HEADERS_SENT then
			io.write("Warning: Cannot modify headers")
		else
			getmetatable(t).__headers[k:lower()] = v
		end
	end,
	__index = function(t,k)
		return getmetatable(t).__headers[k]
	end,
	__headers = {["Content-type"] = "text/html"},
})

function echo(...)
	if not _HEADERS_SENT then
		for i,v in pairs(getmetatable(_HEADERS).__headers) do
			io.write(i,": ",v,"\r\n")
		end
		for i,v in pairs(_SET_COOKIE) do
			io.write("Set-cookie: ",i,"=",encode(v[1]),v[2] and "; expires="..os.date("!%a, %d-%b-%Y %H:%M:%S GMT",v[2]) or "","\r\n")
		end
		io.write("\r\n")
		_HEADERS_SENT = true
		echo = io.write
	end
	io.write(...)
end

function print(...)
	local s = "<pre><code>"
	for i = 1, select('#', ...) do
		s = s..tostring(_G.select(i, ...)).."\t"
	end
	echo(s,"</code></pre>")
end

function tprint(tTable, sTableName, sTab, bComma)
	sTab = sTab or ""
	echo("<pre><code>",sTableName and sTab..sTableName.." = {\n" or sTab.."{\n")
	for key, value in pairs(tTable) do
		local sKey = (type(key) == "string") and ("[%q]"):format(key) or ("[%d]"):format(key)
		if type(value) == "table" then
			tprint(value, sKey, sTab.."\t",true)
		else
			echo(sTab,"\t",sKey," = ",((type(value) == "string") and ('"%s"'):format(value) or tostring(value)),",\n")
		end
	end
	echo(sTab,"}",bComma and ",\n" or "</code></pre>")
end

for _,v in ipairs({"CONTENT_LENGTH", "REQUEST_METHOD", "QUERY_STRING"}) do
	_SERVER[v] = os.getenv(v)
end

setmetatable(_SERVER,{__index = function(t,k)
	if k and not rawget(t,k) then
		local env = os.getenv(k)
		rawset(t,k,env)
		return env
	end
end})

if _SERVER.QUERY_STRING then
	for k,v in (_SERVER.QUERY_STRING.."&"):gmatch("([^&=]-)=([^&=]-)&") do
		k,v = k:lower(),v:gsub("%+"," "):gsub("%%([%da-fA-F][%da-fA-F])",function(char) return string.char(tonumber(char,16)) end)
		_GET[k],_REQUEST[k] = v,v
	end
end

if _SERVER.REQUEST_METHOD and _SERVER.REQUEST_METHOD:upper() == "POST" and _SERVER.CONTENT_LENGTH then
	for k,v in (io.read(tonumber(_SERVER.CONTENT_LENGTH)).."&"):gmatch("([^&=]-)=([^&=]-)&") do
		k,v = k:lower(),v:gsub("%+"," "):gsub("%%([%da-fA-F][%da-fA-F])",function(char) return string.char(tonumber(char,16)) end)
		_POST[k],_REQUEST[k] = v,v
	end
end

setmetatable(_COOKIE,{__index = function(t,k)
	if _SERVER.HTTP_COOKIE then
		for cookie in (_SERVER.HTTP_COOKIE..";"):gmatch("%s*([^;]+)%s*;") do
			local i,v = cookie:match("^([^=]-)%s*=%s*([^=]-)$")
			if i then
				rawset(t,i:lower(),v:gsub("%+"," "):gsub("%%([%da-fA-F][%da-fA-F])",function(char) return string.char(tonumber(char,16)) end))
			else
				rawset(t,cookie:lower(),true)
			end
		end
	end
	setmetatable(_COOKIE,nil)
	return rawget(t,k)
end})